package cn.sunjinxin.trade.domain.service;

import cn.sunjinxin.savior.core.common.Result;
import cn.sunjinxin.savior.ext.container.ExtExecutor;
import cn.sunjinxin.savior.ext.container.ExtRo;
import cn.sunjinxin.trade.domain.ability.PreorderAbility;
import cn.sunjinxin.trade.domain.gateway.ProductRpcGateway;
import cn.sunjinxin.trade.domain.model.Order;
import cn.sunjinxin.trade.domain.model.ProductSku;
import cn.sunjinxin.trade.domain.model.ProductSpu;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @author issavior
 */
@Service
@Slf4j
public class ProductDomainService {

    @Resource
    private ProductRpcGateway productRpcGateway;

    private static final ExecutorService executorService = Executors.newFixedThreadPool(10);

    public ProductSpu getProductSpu(Long productSkuId) {
        // 获取spu
        return productRpcGateway.getProductSpu(productSkuId);
    }

    public ProductSpu getProductSpuWithSplitOrder(Long productSkuId, Integer buyQuantity) {
        // 获取spu
        ProductSpu productSpu = productRpcGateway.getProductSpu(productSkuId);
        return productSpu.of(this.splitOrderPre(productSpu, buyQuantity));
    }

    public List<ProductSku> splitOrderPre(ProductSpu productSpu, Integer buyQuantity) {
        Boolean flag = productSpu.splitFlag();
        List<ProductSku> skus = Lists.newArrayList();
        if (flag) {
            for (int i = 0; i < buyQuantity; i++) {
                for (ProductSku productSku : productSpu.getProductSkus()) {
                    Long quantity = productSku.getQuantity();
                    if (quantity > 1) {
                        for (int j = 0; j < quantity; j++) {
                            ProductSku newSku = new ProductSku(productSku);
                            newSku.setQuantity(1L);
                            skus.add(newSku);
                        }
                    }
                }
            }
        } else {
            for (ProductSku productSku : productSpu.getProductSkus()) {
                Long quantity = productSku.getQuantity();
                for (int i = 0; i < quantity; i++) {
                    ProductSku newSku = new ProductSku(productSku);
                    newSku.setQuantity(buyQuantity * newSku.getQuantity());
                    skus.add(newSku);
                }
            }
        }
        return skus;
    }

    public void preOrder(Order b2bOrder) {
        this.preOrder(b2bOrder.getProductSpu().getProductSkus());
    }

    public void preOrder(List<ProductSku> productSkus) {
        List<Pair<Boolean, ProductSku>> preResult = productSkus.stream().map(sku ->
                        CompletableFuture.supplyAsync(() -> Pair.of(sku.preorder(), sku), executorService)
                                .exceptionally(e -> Pair.of(false, sku)))
                .map(CompletableFuture::join)
                .collect(Collectors.toList());

        if (preResult.stream().anyMatch(pair -> !pair.getLeft())) {
            List<Long> result = preResult.stream().filter(Pair::getLeft).map(Pair::getRight)
                    .map(sku ->
                            CompletableFuture.supplyAsync(() -> Pair.of(sku.replenishment(), sku), executorService)
                                    .exceptionally(e -> Pair.of(false, sku)))
                    .map(CompletableFuture::join)
                    .filter(pair -> !pair.getLeft())
                    .map(Pair::getRight)
                    .map(ProductSku::getProductSkuId)
                    .collect(Collectors.toList());

            if (CollectionUtils.isNotEmpty(result)) {
                log.error("记录失败日志，发送告警，回补失败:{}", result.stream().map(Object::toString).collect(Collectors.joining(",")));
                throw new RuntimeException("回补失败");
            }
        }
    }

}
